const osn = require('obs-studio-node')


const remote = require('@electron/remote');
const WaitQueue = require('wait-queue');
const log = require('./log.js');
const path = require('path')
const fs = require('fs')
const {UtilToolService} = require('./utils')

// import WaitQueue from 'wait-queue';

const getAppPath = remote.app.getAppPath;


const logInfo = (...args) => {
    log.fileLog.debug(args);
};

// import path from 'path';
// Enums
// export const enum EOBSOutputType {
//     Streaming = 'streaming',
//         Recording = 'recording',
//         ReplayBuffer = 'replay-buffer',
// }
//
// export const enum EOBSOutputSignal {
//     Starting = 'starting',
//         Start = 'start',
//         Activate = 'activate',
//         Stopping = 'stopping',
//         Stop = 'stop',
//         Deactivate = 'deactivate',
//         Reconnect = 'reconnect',
//         ReconnectSuccess = 'reconnect_success',
//         Writing = 'writing',
//         Wrote = 'wrote',
//         WriteError = 'writing_error',
// }
//
// export const enum EOBSInputTypes {
//     AudioLine = 'audio_line',
//         ImageSource = 'image_source',
//         ColorSource = 'color_source',
//         Slideshow = 'slideshow',
//         BrowserSource = 'browser_source',
//         FFMPEGSource = 'ffmpeg_source',
//         TextGDI = 'text_gdiplus',
//         TextFT2 = 'text_ft2_source',
//         VLCSource = 'vlc_source',
//         MonitorCapture = 'monitor_capture',
//         WindowCapture = 'window_capture',
//         GameCapture = 'game_capture',
//         DShowInput = 'dshow_input',
//         WASAPIInput = 'wasapi_input_capture',
//         WASAPIOutput = 'wasapi_output_capture',
//         AVCaptureInput = 'av_capture_input',
//         CoreAudioInput = 'coreaudio_input_capture',
//         CoreAudioOutput = 'coreaudio_output_capture',
// }
//
// export const enum EOBSFilterTypes {
//     FaceMask = 'face_mask_filter',
//         Mask = 'mask_filter',
//         Crop = 'crop_filter',
//         Gain = 'gain_filter',
//         Color = 'color_filter',
//         Scale = 'scale_filter',
//         Scroll = 'scroll_filter',
//         GPUDelay = 'gpu_delay',
//         ColorKey = 'color_key_filter',
//         Clut = 'clut_filter',
//         Sharpness = 'sharpness_filter',
//         ChromaKey = 'chroma_key_filter',
//         AsyncDelay = 'async_delay_filter',
//         NoiseSuppress = 'noise_suppress_filter',
//         InvertPolarity = 'invert_polarity_filter',
//         NoiseGate = 'noise_gate_filter',
//         Compressor = 'compressor_filter',
//         Limiter = 'limiter_filter',
//         Expander = 'expander_filter',
//         LumaKey = 'luma_key_filter',
//         NDI = 'ndi_filter',
//         NDIAudio = 'ndi_audiofilter',
//         PremultipliedAlpha = 'premultiplied_alpha_filter',
//         VST = 'vst_filter',
// }
//
// export const enum EOBSTransitionTypes {
//     Cut = 'cut_transition',
//         Fade = 'fade_transition',
//         Swipe = 'swipe_transition',
//         Slide = 'slide_transition',
//         Stinger = 'obs_stinger_transition',
//         FadeToColor = 'fade_to_color_transition',
//         Wipe = 'wipe_transition',
// }
//
// export const enum EOBSSettingsCategories {
//     General = 'General',
//         Stream = 'Stream',
//         StreamSecond = 'StreamSecond',
//         Output = 'Output',
//         Audio = 'Audio',
//         Video = 'Video',
//         Hotkeys = 'Hotkeys',
//         Advanced = 'Advanced',
// }
//
// // Interfaces
// export interface IPerformanceState {
//     CPU: number;
//     numberDroppedFrames: number;
//     percentageDroppedFrames: number;
//     streamingBandwidth: number;
//     streamingDataOutput: number;
//     recordingBandwidth: number;
//     recordingDataOutput: number;
//     frameRate: number;
//     averageTimeToRenderFrame: number;
//     memoryUsage: number;
//     diskSpaceAvailable: string;
// }
//
// export interface IOBSOutputSignalInfo {
//     type: EOBSOutputType;
//     signal: EOBSOutputSignal;
//     code: osn.EOutputCode;
//     error: string;
//     service: string;
// }
//
// export interface IConfigProgress {
//     event: TConfigEvent;
//     description: string;
//     percentage?: number;
//     continent?: string;
// }
//
// export interface IVec2 {
//     x: number;
//     y: number;
// }
//
// export interface ICrop {
//     top: number;
//     bottom: number;
//     left: number;
//     right: number;
// }
//
// // Types
// export type TOBSHotkey = {
//     ObjectName: string;
//     ObjectType: osn.EHotkeyObjectType;
//     HotkeyName: string;
//     HotkeyDesc: string;
//     HotkeyId: number;
// };
//
// export type TConfigEvent = 'starting_step' | 'progress' | 'stopping_step' | 'error' | 'done';

function fixPathWhenPackaged(p) {
    return p.replace('app.asar', 'app.asar.unpacked');
}

const appPath = getAppPath();

class OBSHandler {
    // Variables for obs initialization

    signals = new WaitQueue();
    inputTypes=[];
    filterTypes=[];
    transitionTypes=[];
    os='';
    userStreamKey=null;
    defaultVideoContext=null;
    workingDirectory = fixPathWhenPackaged(
        path.join(appPath, 'node_modules', 'obs-studio-node'),
    );
    language = 'en-US';
    obsPath = fixPathWhenPackaged(path.join(appPath, 'runtime/osn-data'));
    pipeName = 'osn-tests-pipe-'.concat(UtilToolService.createUUID());
    version = '0.00.00-preview.0';
    crashServer = '';
    osnTestName;
    progress = new WaitQueue();

    constructor(testName, needDefaultVideoContext = true) {
        this.os = process.platform;
        this.osnTestName = testName;
        // this.cacheUploader = new CacheUploader(testName, this.obsPath);
        this.startup();
        if (needDefaultVideoContext) {
            this.createDefaultVideoContext();
        }
        this.inputTypes = osn.InputFactory.types();
        const index = this.inputTypes.indexOf('syphon-input', 0);
        if (index > -1) {
            this.inputTypes.splice(index, 1);
        }
        this.filterTypes = osn.FilterFactory.types();
        this.transitionTypes = osn.TransitionFactory.types();
    }

    startup() {
        let initResult;
        logInfo(this.osnTestName, 'Initializing OBS');

        try {
            const appPath = getAppPath();
            console.log(`appPath is ${appPath}`);
            const __packageDir = path.resolve(appPath, 'node_modules/obs-studio-node');
            console.log(`__packageDir is ${__packageDir}`);

            var IPC = osn.NodeObs.IPC;
            if (
                fs.existsSync(path.resolve(__packageDir, '/bin').replace('app.asar', 'app.asar.unpacked'))
            ) {
                IPC.setServerPath(
                    path.resolve(__dirnameApple, `obs64`).replace('app.asar', 'app.asar.unpacked'),
                    path.resolve(__dirnameApple).replace('app.asar', 'app.asar.unpacked'),
                );
            } else if (
                fs.existsSync(path.resolve(__packageDir, `obs64.exe`).replace('app.asar', 'app.asar.unpacked'))
            ) {
                //主要是这里，设置了OBS.exe路径和工作路径node_modules/obs-studio-node

                // console.log(path.resolve(__packageDir, `obs64.exe`))
                IPC.setServerPath(
                    path.resolve(__packageDir, `obs64.exe`).replace('app.asar', 'app.asar.unpacked'),
                    path.resolve(__packageDir).replace('app.asar', 'app.asar.unpacked'),
                );
            } else {
                IPC.setServerPath(
                    path.resolve(__packageDir, `obs32.exe`).replace('app.asar', 'app.asar.unpacked'),
                    path.resolve(__packageDir).replace('app.asar', 'app.asar.unpacked'),
                );
            }

            console.log(`this.pipeName is ${this.pipeName}`);
            IPC.host(this.pipeName);
            console.log(`workingDirectory is ${this.workingDirectory}`);
            osn.NodeObs.SetWorkingDirectory(this.workingDirectory);

            console.log({
                language: this.language,
                obsPath: this.obsPath,
                version: this.version,
                crashServer: this.crashServer,
            });
            initResult = osn.NodeObs.OBS_API_initAPI(
                this.language,
                this.obsPath,
                this.version,
                this.crashServer,
            );

            console.log({initResult});


        } catch (e) {
            throw Error('Exception when initializing OBS process: ' + e);
        }

        console.log({osn});
        if (initResult != 0) {
            throw Error('OBS process initialization failed with code ' + initResult);
        }

        logInfo(this.osnTestName, 'OBS started successfully');
    }

    shutdown() {
        if (this.defaultVideoContext) {
            this.destroyDefaultVideoContext();
        }

        logInfo(this.osnTestName, 'Shutting down OBS');

        try {
            osn.NodeObs.OBS_service_removeCallback();
            osn.NodeObs.IPC.disconnect();
        } catch (e) {
            throw Error('Exception when shutting down OBS process: ' + e);
        }

        logInfo(this.osnTestName, 'OBS shutdown successfully');
    }

    setStreamKey(value) {
        const service = osn.ServiceFactory.legacySettings;
        service.update({key: value});
        osn.ServiceFactory.legacySettings = service;
        this.setSetting(EOBSSettingsCategories.Stream, 'key', value);
    }

    getStreamKey() {
        const service = osn.ServiceFactory.legacySettings;
        return service.settings.key;
    }

    setSetting(category, parameter, value) {
        let oldValue;

        // Getting settings container
        const settings = osn.NodeObs.OBS_settings_getSettings(category).data;

        settings.forEach(subCategory => {
            subCategory.parameters.forEach(param => {
                if (param.name === parameter) {
                    oldValue = param.currentValue;
                    param.currentValue = value;
                }
            });
        });

        // Saving updated settings container
        if (value != oldValue) {
            osn.NodeObs.OBS_settings_saveSettings(category, settings);
        }
    }

    getSetting(category, parameter) {
        let value;

        // Getting settings container
        const settings = osn.NodeObs.OBS_settings_getSettings(category).data;

        // Getting parameter value
        settings.forEach(subCategory => {
            subCategory.parameters.forEach(param => {
                if (param.name === parameter) {
                    value = param.currentValue;
                }
            });
        });

        return value;
    }

    setSettingsContainer(category, settings) {
        osn.NodeObs.OBS_settings_saveSettings(category, settings);
    }

    getSettingsContainer(category) {
        return osn.NodeObs.OBS_settings_getSettings(category).data;
    }

    connectOutputSignals() {
        osn.NodeObs.OBS_service_connectOutputSignals((signalInfo) => {
            this.signals.push(signalInfo);
        });
    }

    getNextSignalInfo(output, signal) {
        return new Promise((resolve, reject) => {
            this.signals.shift().then(function(signalInfo) {
                resolve(signalInfo);
            });
            setTimeout(
                () =>
                    reject(
                        new Error(
                            output.replace(/^\w/, c => c.toUpperCase()) + ' ' + signal + ' signal timeout',
                        ),
                    ),
                30000,
            );
        });
    }

    startAutoconfig() {
        osn.NodeObs.InitializeAutoConfig(
            (progressInfo) => {
                if (
                    progressInfo.event == 'stopping_step' ||
                    progressInfo.event == 'done' ||
                    progressInfo.event == 'error'
                ) {
                    this.progress.push(progressInfo);
                }
            },
            {
                service_name: 'Twitch',
            },
        );
    }

    getNextProgressInfo(autoconfigStep) {
        return new Promise((resolve, reject) => {
            this.progress.shift().then(function(progressInfo) {
                resolve(progressInfo);
            });
            setTimeout(() => reject(new Error(autoconfigStep + ' step timeout')), 50000);
        });
    }

    createDefaultVideoContext() {
        logInfo(this.osnTestName, 'createDefaultVideoContext called');
        this.defaultVideoContext = osn.VideoFactory.create();
        const defaultVideoInfo = {
            fpsNum: 60,
            fpsDen: 1,
            baseWidth: 1280,
            baseHeight: 720,
            outputWidth: 1080,
            outputHeight: 420,
            // outputFormat: osn.EVideoFormat.NV12,
            // colorspace: osn.EColorSpace.CS709,
            // range: osn.ERangeType.Partial,
            // scaleType: osn.EScaleType.Bilinear,
            // fpsType: osn.EFPSType.Fractional,
            outputFormat: 2,
            colorspace: 2,
            range: 1,
            scaleType: 3,
            fpsType: 2,
        };
        this.defaultVideoContext.video = defaultVideoInfo;
    }

    destroyDefaultVideoContext() {
        this.defaultVideoContext.destroy();
        this.defaultVideoContext = null;
    }

    skipSource(inputType) {
        if (process.platform === 'darwin') {
            if (
                inputType === 'browser_source' ||
                inputType === 'window_capture' ||
                inputType === 'monitor_capture' ||
                inputType === 'display_capture' ||
                inputType === 'screen_capture' ||
                inputType === 'coreaudio_input_capture' ||
                inputType === 'coreaudio_output_capture'
            ) {
                return true;
            }
        }
        return false;
    }
}

module.exports = {
    OBSHandler
}
